import AABB from "./AABB";
import QuadTree from "./QuadTree";
import EventUtil from "./event/EventUtil";
import { Events } from "./event/Event";

// QuadTreeNode
export default class QuadTreeNode {

  // 表示区域
  private mRectBounding: cc.Rect = null;

  // 叶子节点
  public mArrLeafNode: QuadTreeNode[] = [];

  // 存储的对象
  public mArrObject: AABB[] = [];

  // 父节点
  private mParentNode: QuadTreeNode = null;

  // 自身的深度
  private mNumDepth: number = null;

  // 自身深度路径
  public mStrDepthPath: string = null;

  // 自身index
  private mNumSiblingIndex: number = null;

  constructor(pBounding: cc.Rect, pParentNode: QuadTreeNode, pNumSiblingIndex: number) {
    this.mRectBounding = new cc.Rect(
      pBounding.x,
      pBounding.y,
      pBounding.width,
      pBounding.height,
    );

    this.mParentNode = pParentNode;
    if (!pParentNode) {
      // 根节点
      this.mNumDepth = 0;
      this.mStrDepthPath = "0";
      this.mNumSiblingIndex = 0;
    } else {
      // 非根节点
      this.mNumDepth = pParentNode.Depth + 1;
      this.mNumSiblingIndex = pNumSiblingIndex;
      this.mStrDepthPath = pParentNode.DepthPath + "_" + pNumSiblingIndex;
    }

    EventUtil.Dispatcher.emit(Events.EVENT_DRAW_RECT, { rect: pBounding, depth: this.mNumDepth });
  }

  public get Depth(): number {
    return this.mNumDepth;
  }

  public get DepthPath(): string {
    return this.mStrDepthPath;
  }

  // 插入节点
  public insert(pObject: AABB) {

    if (this.mArrLeafNode.length > 0) {
      // 存在叶子节点的情况

      // 直接往叶子节点插入
      const arrQuadrant = this.getQuadrant(pObject.BoundingBox);
      for (let i = 0; i < arrQuadrant.length; i++) {
        const numLeafIndex = arrQuadrant[i];
        this.mArrLeafNode[numLeafIndex].insert(pObject);
      }
    } else {
      // 不存在叶子节点的情况

      // 添加对象
      this.addObject(pObject);

      // 添加之后，超过单个节点所能承受的最大上限且未超深度上限就要分裂
      if (this.mArrObject.length >= QuadTree.sNumMaxObject && this.mNumDepth < QuadTree.sNumMaxDepth) {

        // 分裂为4个子树
        this.split();

        // 将子对象转移至各个子树中
        let i = 0;
        while (this.mArrObject[i]) {
          const subObject: AABB = this.mArrObject[i];
          if (!this.delObject(subObject)) {
            i++;
          }
          const arrQuadrant = this.getQuadrant(subObject.BoundingBox);
          for (let j = 0; j < arrQuadrant.length; j++) {
            const numLeafIndex = arrQuadrant[j];
            this.mArrLeafNode[numLeafIndex].insert(subObject);
          }
        }

        // 对象只允许存储在叶子节点中
        this.mArrObject = [];
      }
    }
  }

  public retrieve(pRect: cc.Rect): AABB[] {
    let arrResult: AABB[] = [];
    arrResult = this.mArrObject;

    // 追加叶子节点中的对象
    if (this.mArrLeafNode.length) {
      const arrQuadrant = this.getQuadrant(pRect);
      for (let i = 0; i < arrQuadrant.length; i++) {
        const numLeafIndex = arrQuadrant[i];
        arrResult = arrResult.concat(this.mArrLeafNode[numLeafIndex].retrieve(pRect));
      }
    }

    arrResult = arrResult.filter((item, index) => {
      return arrResult.indexOf(item) >= index;
    });

    return arrResult;
  }

  // 分裂
  private split() {
    const numMidX = this.mRectBounding.x + this.mRectBounding.width / 2;
    const numMidY = this.mRectBounding.y + this.mRectBounding.height / 2;
    const numW = this.mRectBounding.width / 2;
    const numH = this.mRectBounding.height / 2;

    // 0号叶子
    const rect0: cc.Rect = new cc.Rect(
      numMidX,
      numMidY,
      numW,
      numH,
    );
    this.mArrLeafNode[0] = new QuadTreeNode(rect0, this, 0);

    // 1号叶子
    const rect1: cc.Rect = new cc.Rect(
      this.mRectBounding.x,
      numMidY,
      numW,
      numH,
    );
    this.mArrLeafNode[1] = new QuadTreeNode(rect1, this, 1);

    // 2号叶子
    const rect2: cc.Rect = new cc.Rect(
      this.mRectBounding.x,
      this.mRectBounding.y,
      numW,
      numH,
    );
    this.mArrLeafNode[2] = new QuadTreeNode(rect2, this, 2);

    // 3号叶子
    const rect3: cc.Rect = new cc.Rect(
      numMidX,
      this.mRectBounding.y,
      numW,
      numH,
    );
    this.mArrLeafNode[3] = new QuadTreeNode(rect3, this, 3);
  }

  // 获取 pBounding 占据 this.mRectBound 的象限
  private getQuadrant(pBounding: cc.Rect) {

    const arrQuadrant = [];

    // 中心点坐标
    const numMidX = this.mRectBounding.x + this.mRectBounding.width / 2;
    const numMidY = this.mRectBounding.y + this.mRectBounding.height / 2;

    const isStartLeft = pBounding.x < numMidX;
    const isEndRight = pBounding.x + pBounding.width >= numMidX;
    const isStartLower = pBounding.y < numMidY;
    const isEndUpper = pBounding.y + pBounding.height >= numMidY;

    // 第一象限有交集
    if (isEndRight && isEndUpper) {
      arrQuadrant.push(0);
    }

    // 第二象限有交集
    if (isStartLeft && isEndUpper) {
      arrQuadrant.push(1);
    }

    // 第三象限有交集
    if (isStartLeft && isStartLower) {
      arrQuadrant.push(2);
    }

    // 第三象限有交集
    if (isEndRight && isStartLower) {
      arrQuadrant.push(3);
    }
    return arrQuadrant;
  }

  // 增加对象
  public addObject(pObject: AABB) {
    const index = this.mArrObject.indexOf(pObject);
    if (index === -1) {
      this.mArrObject.push(pObject);
    }

    // 将路径信息记录到对象身上
    pObject.addQuadnodeRecord(this);
  }

  // 删除对象
  public delObject(pObject: AABB, pIsDelRecord: boolean = true): boolean {

    let isDel = false;

    const index = this.mArrObject.indexOf(pObject);
    if (index !== -1) {
      this.mArrObject.splice(index, 1);
      isDel = true;
    }

    if (pIsDelRecord) {
      // 将路径信息记录到对象身上
      pObject.delQuadnodeRecord(this);
    }

    return isDel;
  }
}
